#pragma once

#include "gl.h"
#include "texture.h"
#include "../../base/obj_pointer.h"
#include "../../container/ordered_map.h"
#include "../../vmath/vec2.h"
#include "../../vmath/vec3.h"
#include "../../vmath/vec4.h"
#include "../../vmath/mat4.h"
#include <unordered_map>

OPEN_JLIB_NS


struct Shader : public Obj {

  ~Shader () override;

  bool init (const c8* vert, const c8* frag, const c8* geom = nullptr);
  void clear () noexcept;

  inline GLuint id () const noexcept { return _id; }
  inline void use () { glUseProgram(_id); }

  template<GLint Location>
  inline void u1i (s32 i1) { glUniform1i(Location, i1); }
  template<GLint Location>
  inline void u2i (s32 i1, s32 i2) { glUniform2i(Location, i1, i2); }
  template<GLint Location>
  inline void u3i (s32 i1, s32 i2, s32 i3) { glUniform3i(Location, i1, i2, i3); }
  template<GLint Location>
  inline void u4i (s32 i1, s32 i2, s32 i3, s32 i4) { glUniform4i(Location, i1, i2, i3, i4); }

  template<GLint Location>
  inline void u1u (u32 i1) { glUniform1ui(Location, i1); }
  template<GLint Location>
  inline void u2u (u32 i1, u32 i2) { glUniform2ui(Location, i1, i2); }
  template<GLint Location>
  inline void u3u (u32 i1, u32 i2, u32 i3) { glUniform3ui(Location, i1, i2, i3); }
  template<GLint Location>
  inline void u4u (u32 i1, u32 i2, u32 i3, u32 i4) { glUniform4ui(Location, i1, i2, i3, i4); }

  template<GLint Location>
  inline void u1f (f32 v) { glUniform1f(Location, v);  }
  template<GLint Location>
  inline void u2f (Vec2 v) { glUniform2f(Location, v.x(), v.y()); }
  template<GLint Location>
  inline void u3f (Vec3 v) { glUniform3f(Location, v.x(), v.y(), v.z()); }
  template<GLint Location>
  inline void u4f (Vec4 v) { glUniform4f(Location, v.x(), v.y(), v.z(), v.w()); }

  template<GLint Location, GLsizei Count = 1>
  inline void u1fv (const f32 *v) { glUniform1fv(Location, Count, v);  }
  template<GLint Location, GLsizei Count = 1>
  inline void u2fv (const Vec2 *v) { glUniform2fv(Location, Count, (const GLfloat*)&v); }
  template<GLint Location, GLsizei Count = 1>
  inline void u3fv (const Vec3 *v) { glUniform3fv(Location, Count, (const GLfloat*)&v); }
  template<GLint Location, GLsizei Count = 1>
  inline void u4fv (const Vec4 *v) { glUniform4fv(Location, Count, (const GLfloat*)&v); }


  template<GLint Location, GLsizei Count = 1, GLboolean Transpose = GL_FALSE>
  inline void u4m (const Mat4 *m) { glUniformMatrix4fv(Location, Count, Transpose, (GLfloat*)m); }

protected:
  bool compile (GLuint &shader, GLenum type, const GLchar *source);

  GLuint _id {0};
};


CLOSE_JLIB_NS
